#include "io.hpp"

extern std::map<uint64_t, Semaphore<std::array<char, 4096>>> memory;
static int portAvail = 2; // 允许分配的最小端口号，遵循PID分配方式

void IODevice::setStatus(IOSTATUS sID)
{
	status = sID;
}
void IODevice::start(uint64_t &val)
{
	if (status != READY)
		return;
	switch (type)
	{
	case IN:
		read();
		val = get();
		break;
	case OUT:
		set(val);
		write();
		break;
	case FIN:
		set(val);
		read();
		break;
	case FOUT:
		set(val);
		write();
		break;
	case FIO:
		if (status == READ)
			read();
		else if (status == WRITE)
			write();
		break;
	default:
		setStatus(FAIL);
		break;
	}
}
void SerialIn::open(std::string name, IOTYPE ftype)
{
	if ((name != "") || (ftype != IN))
	{
		setStatus(FAIL);
		return;
	}
}
void SerialIn::read()
{
	value = getchar();
	setStatus(DONE);
}
void SerialIn::write()
{
	setStatus(FAIL);
}
void SerialOut::open(std::string name, IOTYPE type)
{
	if ((name != "") || (type != OUT))
	{
		setStatus(FAIL);
		return;
	}
}
void SerialOut::read()
{
	setStatus(FAIL);
}
void SerialOut::write()
{
	putchar(value);
	setStatus(DONE);
}
void FileIn::open(std::string name, IOTYPE type)
{
	if (type != FIN)
	{
		setStatus(FAIL);
		return;
	}
	fin = fopen(name.c_str(), "rb");
}
void FileIn::read()
{
	if (status != READY)
		;
	fseek(fin, faddr, 0);
	memory[maddr >> 12].w_lock();
	for (int i = 0; i < 4096; i++)
		memory[maddr >> 12].get()[i] = fgetc(fin);
	memory[maddr >> 12].w_unlock();
	setStatus(DONE);
}
void FileIn::write()
{
	setStatus(FAIL);
}
void FileOut::open(std::string name, IOTYPE type)
{
	if (type != FOUT)
	{
		setStatus(FAIL);
		return;
	}
	fout = fopen(name.c_str(), "wb");
}
void FileOut::read()
{
	setStatus(FAIL);
}
void FileOut::write()
{
	if (status != READY)
		;
	fseek(fout, faddr, 0);
	memory[maddr >> 12].rlock();
	for (int i = 0; i < 4096; i++)
		fputc(memory[maddr >> 12].get()[i], fout);
	memory[maddr >> 12].r_unlock();
	setStatus(DONE);
}
void FileIO::open(std::string name, IOTYPE type)
{
	if (type != FIO)
	{
		setStatus(FAIL);
		return;
	}
	file = fopen(name.c_str(), "rb+");
}
void FileIO::read()
{
	if (status != READY)
		;
	fseek(file, faddr, 0);
	memory[maddr >> 12].w_lock();
	for (int i = 0; i < 4096; i++)
		memory[maddr >> 12].get()[i] = fgetc(file);
	memory[maddr >> 12].w_unlock();
	setStatus(DONE);
}
void FileIO::write()
{
	if (status != READY)
		;
	fseek(file, faddr, 0);
	memory[maddr >> 12].rlock();
	for (int i = 0; i < 4096; i++)
		fputc(memory[maddr >> 12].get()[i], file);
	memory[maddr >> 12].r_unlock();
	setStatus(DONE);
}
void IOChannel::init()
{
	mIO[0] = new SerialIn();  // using std::cin
	mIO[1] = new SerialOut(); // using std::cout
}
void IOChannel::mount(std::string name, IOTYPE type)
{
	if (portAvail < 65536)
	{
	alloc:
		switch (type)
		{
		case IN:
			mIO[portAvail] = new SerialIn();
			break;
		case OUT:
			mIO[portAvail] = new SerialOut();
			break;
		case FIN:
			mIO[portAvail] = new FileIn();
			break;
		case FOUT:
			mIO[portAvail] = new FileOut();
			break;
		case FIO:
			mIO[portAvail] = new FileIO();
			break;
		default:
			break;
		}
		mIO[portAvail]->setStatus(READY);
		mIO[portAvail]->open(name, type);
		portAvail += 1;
	}
	else
	{
		for (portAvail = 2; portAvail < 65536; portAvail++)

			if (mIO[portAvail] == nullptr)
				goto alloc;
	}
}
void IOChannel::umount(uint16_t port)
{
	delete mIO[port];
	mIO.erase(port);
}
void IOChannel::run()
{
	while (1)
	{
		msg->start.lock();
		if (msg->shutdown)
			return;
		if (mIO[msg->port] == nullptr)
			;
		else
		{
			if (msg->dir) // 从IO读取
			{
				if (!msg->isData)
					mIO[msg->port]->getStatus();
				else
					mIO[msg->port]->start(msg->addr);
			}
			else
			{
				if (!msg->isData)
					mIO[msg->port]->setStatus(IOSTATUS(msg->addr & 0xff));
				else
					mIO[msg->port]->start(msg->addr);
			}
		}
		msg->end.unlock();
	}
}